package mmodel

import "sort"

type List[T any] []T

func NewList[T any](data []T) *List[T] {
	res := List[T](data)
	return &res
}
func NewStream[T any](data []T) *Stream[T] {
	list := List[T](data)
	return &Stream[T]{
		list: &list,
	}
}
func NewNilList[T any]() *List[T] {
	res := List[T](make([]T, 0))
	return &res
}
func (ce *List[T]) Append(data ...T) *List[T] {
	*ce = append(*ce, data...)
	return ce
}
func (ce *List[T]) Sorter(fun func(r, l T) bool) *List[T] {
	sort.Slice(*ce, func(i, j int) bool {
		return fun((*ce)[i], (*ce)[j])
	})
	return ce
}
func (ce *List[T]) Stream() *Stream[T] {

	return &Stream[T]{
		list: ce,
	}
}

type filter[T any] func(i int, item T) bool
type Stream[T any] struct {
	list *List[T]
	step []any
}

func (ce *Stream[T]) Filter(fun filter[T]) *Stream[T] {
	ce.step = append(ce.step, fun)
	return ce
}

func (ce *Stream[T]) District(fun func(i int, item T) string) *Stream[T] {
	list := make([]T, 0)
	m := map[string]bool{}
	for i := range *ce.list {
		if ce.doFilter(i) {
			k := fun(i, (*ce.list)[i])
			if m[k] {
				continue
			} else {
				m[k] = true
				list = append(list, (*ce.list)[i])
			}

		}
	}
	ls := List[T](list)
	ce.list = &ls
	ce.step = nil
	return ce
}

func (ce *Stream[T]) Sorter(fun func(r, l T) bool) *Stream[T] {
	list := make([]T, 0)
	for i := range *ce.list {
		if ce.doFilter(i) {
			list = append(list, (*ce.list)[i])
		}
	}
	sort.Slice(list, func(i, j int) bool {
		return fun(list[i], list[j])
	})
	ls := List[T](list)
	ce.list = &ls
	ce.step = nil
	return ce
}

func (ce *Stream[T]) Each(fun func(i int, item T)) {
	for i := range *ce.list {
		if ce.doFilter(i) {
			fun(i, (*ce.list)[i])
		}
	}
}
func (ce *Stream[T]) ToMap(fun func(i int, item T) string) map[string]T {
	m := map[string]T{}
	for i := range *ce.list {
		if ce.doFilter(i) {
			k := fun(i, (*ce.list)[i])
			m[k] = (*ce.list)[i]
		}
	}
	return m
}
func (ce *Stream[T]) Group(fun func(i int, item T) string) map[string][]T {
	m := map[string][]T{}
	for i := range *ce.list {
		if ce.doFilter(i) {
			k := fun(i, (*ce.list)[i])
			if m[k] == nil {
				m[k] = append(make([]T, 0), (*ce.list)[i])
			} else {
				m[k] = append(m[k], (*ce.list)[i])
			}
		}
	}
	return m
}
func (ce *Stream[T]) Result() []T {
	res := make([]T, 0)
	for i := range *ce.list {
		if ce.doFilter(i) {
			res = append(res, (*ce.list)[i])
		}
	}
	return res
}
func (ce *Stream[T]) List() *List[T] {
	res := make([]T, 0)
	for i := range *ce.list {
		if ce.doFilter(i) {
			res = append(res, (*ce.list)[i])
		}
	}
	return NewList(res)
}

func (ce *Stream[T]) doFilter(i int) bool {

	for _, step := range ce.step {
		if f, ok := step.(filter[T]); ok {
			if !f(i, (*ce.list)[i]) {
				return false
			}
		}
	}
	return true
}
